home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume2 / saverate < prev    next >
Encoding:
Internet Message Format  |  1991-08-07  |  13.4 KB

  1. From: dennis@rlgvax.UUCP (Dennis.Bednar)
  2. Newsgroups: comp.sources.misc
  3. Subject: v02i095: saverate.c - program to calculate interest rate from periodic savings
  4. Message-ID: <914@rlgvax.UUCP>
  5. Date: 13 Apr 88 06:10:38 GMT
  6. Approved: allbery@ncoast.UUCP
  7.  
  8. comp.sources.misc: Volume 2, Issue 95
  9. Submitted-By: "Dennis.Bednar" <dennis@rlgvax.UUCP>
  10. Archive-Name: saverate
  11.  
  12. Below is a program that will compute the interest rate from periodic
  13. savings.  The default is to deposit the payment at the beginning of
  14. each period, but this can be overridden with the -e flag (stands for
  15. "end" of period).  More details on useage in the help file included.
  16.  
  17. Misc.invest readers, this program will derive the annual percentages
  18. reported in "29 Yr Performance of 20th Century, Select, Growth, Funds".
  19.  
  20. For example, depositing $166.66 at the beginning of every month, for
  21. 29 years (348 periods), with a future value of $874,519, you would
  22. type:
  23.  
  24.     saverate 348 166.66 874519 12
  25.  
  26. (the last argument, 12, is the number of periods (months here) per year)
  27.  
  28. and it will yield the answer of:
  29.  
  30. 14.37943891% per year, Payment deposited at beginning of each period
  31.  
  32. PS, I have verified that the numbers agree with the TI BA-II business
  33. calculator.
  34.  
  35. -enjoy
  36. -dennis
  37.  
  38.  
  39. #--------------- CUT HERE ---------------
  40. #! /bin/sh
  41. # This is a shell archive, meaning:
  42. # 1. Remove everything above the #! /bin/sh line.
  43. # 2. Save the resulting text in a file.
  44. # 3. Execute the file with /bin/sh (not csh) to create the files:
  45. #    getopt.h
  46. #    saverate.c
  47. #    saverate.help
  48. # This archive created: Tue Apr 12 22:26:10 EDT 1988
  49. #
  50. if test -f getopt.h
  51. then
  52. echo shar: will not over-write existing file 'getopt.h'
  53. else
  54. echo x - getopt.h
  55. # ............    F  I   L   E      B  E  G  .......... getopt.h
  56. cat << '\SHAR_EOF' > getopt.h
  57. extern    int    getopt();    /* get next option argument        */
  58. extern    char    *optarg;    /* start of option after rtn from getopt() */
  59. extern    int    optind;        /* argv[i] to be processed by next getopt() */
  60. extern    int    opterr;        /* set to !0 to disable ? stderr errors    */
  61. \SHAR_EOF
  62. # ............    F  I   L   E      E  N  D  .......... getopt.h
  63. fi # end of overwriting check
  64. if test -f saverate.c
  65. then
  66. echo shar: will not over-write existing file 'saverate.c'
  67. else
  68. echo x - saverate.c
  69. # ............    F  I   L   E      B  E  G  .......... saverate.c
  70. cat << '\SHAR_EOF' > saverate.c
  71. /*
  72.  * saverate.c
  73.  * dennis bednar 04 12 88
  74.  * {uunet|sundc}!rlgvax!dennis
  75.  *
  76.  * compute rate of return on savings account with equal deposits
  77.  * every period, either at the beginning or end of each period.
  78.  * "annuity due" is savings at the beginning of each period.
  79.  * "ordinary annuity" is savings at the end of each period.
  80.  *
  81.  * These are the unknowns:
  82.  *    total periods
  83.  *    payment per period (into the savings account)
  84.  *    future value
  85.  *    [optional] periods per year
  86.  *
  87.  * It will compute the interest per period, except that if the number
  88.  * of periods per year is given, then it is multiplied by that to get
  89.  * the percent per year.
  90.  *
  91.  * cmd [-de -t#] tot_periods pmt_per_period future_value [periods_per_year]
  92.  *    -d = debug
  93.  *    -e = deposit at end of each period
  94.  *    -t# = max number of times "Try to guess" the rate, default DEF_TRY,
  95.  *          (will speed up the algorithm if you have slow machine,
  96.           at the expense of less accuracy)
  97.  *
  98.  * To try:
  99.  *    cmd 4 100 430.913581 4
  100.  *    should give 12% a year, deposit at begin of each period:
  101.  *        Here you add $100 at the beginning of each quarter
  102.  *        for 4 consecutive quarter.  The money earns 3% per
  103.  *        quarter, so that the total you have (430.912) is
  104.  *        computed as follows:
  105.  *            100 * 1.03^4 = 112.55088100
  106.  *            100 * 1.03^3 = 109.2727
  107.  *            100 * 1.03^2 = 106.09
  108.  *            100 * 1.03   = 103
  109.  *        Total              = 430.91358100
  110.  *
  111.  *    cmd 4 100 418.362 4
  112.  *    should also give 12% a year, deposit at end of each period.
  113.  *
  114.  * Periods_per_year defaults to 1.
  115.  *
  116.  * Formula for "annuity due" (deposit at begin of each period):
  117.  *
  118.  *          (1+r)^(N+1) - (1+r)
  119.  * FV = pmt * ------------------    where r != 0    (1)
  120.  *           (r)
  121.  *
  122.  * FV = pmt * N                where r == 0.    (2)
  123.  *
  124.  * Formula for "normal annuity" (deposit at end of each period):
  125.  *
  126.  *          (1+r)^N - 1
  127.  * FV = pmt * ------------------    where r != 0    (3)
  128.  *           (r)
  129.  *
  130.  * FV = pmt * N                where r == 0.    (4)
  131.  *
  132.  *
  133.  * where
  134.  *    fv = future value, amount in account after N periods
  135.  *    pmt = payment at begin of each period
  136.  *    r = interest rate per period (eg, .01 means 1% interest per period,
  137.  *        this would have to be multiplied by 12 to get 12%/yr if each
  138.  *        period were one month)
  139.  *    N = total number of periods money is compounding interest.
  140.  *
  141.  * Derivation:
  142.  *    FV = (pmt * (1+r)^N) + (pmt * (1+r)^(N-1)) + ... + (pmt * (1+r)) )
  143.  *    we can factor out pmt for each term on the right, so
  144.  *    FV = pmt * [ (1+r)^N + ... + (1+r)]            (5)
  145.  *
  146.  * usual mathematical trick is to multiply both sides of (5) by (1+r),
  147.  * obtaining equation (6), then subtract (5) from (6), to obtain
  148.  * equation (7):
  149.  *
  150.  *    FV * (1+r) = pmt * [ (1+r)^(N+1) + ... (1+r)^2]        (6)
  151.  *
  152.  *    FV * r = pmt * [ (1+r)^(N+1) - (1+r)]            (7)
  153.  *
  154.  * Divide both side of (7) by r to get equation (1).  The only problem
  155.  * is that the division will blow up if r == 0, which is why equation
  156.  * (2) was mentioned.
  157.  *
  158.  * Derivation of equations (3) & (4) are to be proved by the reader,
  159.  * using a similar technique.
  160.  *
  161.  */
  162. #include <stdio.h>
  163. #include "getopt.h"
  164.  
  165. #define DEBUG 1
  166. #define FLOAT double
  167. #define STR_SAME !strcmp
  168.  
  169. extern    FLOAT    atof();
  170.  
  171. /* forward refs to non-int functions */
  172. FLOAT    get_rate();
  173. FLOAT    r_abs();
  174. FLOAT    get_fv();
  175. FLOAT    raise();
  176. FLOAT    ask_f();
  177.  
  178. char    *cmd;
  179. int    debug = 0;
  180. #define T_ANNUITY_DUE    0    /* pmt made at begin of each period */
  181. #define T_NORMAL_ANN    1    /* pmt made at end of each period */
  182. int    annuity_type = T_ANNUITY_DUE;
  183. #define DEF_TRY    100        /* default number of tries */
  184. int    MAX_TRY = DEF_TRY;
  185.  
  186. main( argc, argv )
  187.     int    argc;
  188.     char    *argv[];
  189. {
  190.     int    tot_per;    /* total periods */
  191.     FLOAT    rate,        /* rate per period, initially */
  192.         pmt,        /* payment per period */
  193.         fv;        /* future value */
  194.     int    per_per_yr,    /* periods per year */
  195.         num_opts;    /* number of options arguments */
  196.     int    yes;
  197.  
  198.     cmd = argv[0];
  199.  
  200.     num_opts= get_args( argc, argv ) - 1;
  201.  
  202.     /* skip over the options as though they weren't even there */
  203.     argc -= num_opts;
  204.     argv += num_opts;
  205.  
  206.     /* argc and argv now assume as if all options have been stripped out,
  207.      * except that argv[0] might not be the command any more.  Hence,
  208.      * refer to argv[0] via "cmd" saved above.
  209.      */
  210.     if (argc >= 4)
  211.         {
  212.         tot_per = atoi( argv[1] );
  213.         pmt = atof( argv[2] );
  214.         fv = atof( argv[3] );
  215.         if (argc > 5)
  216.             usage();
  217.         else if (argc == 5)
  218.             per_per_yr = atoi( argv[4] );
  219.         else
  220.             per_per_yr = 1;
  221.         }
  222.     else if (argc == 1)    /* just the command and possible args, no numbers */
  223.         {
  224.         tot_per = ask_i( "Total Number of Periods" );
  225.         pmt = ask_f( "Payment Deposited Per Period" );
  226.         fv = ask_f( "Future Value (after the last Period)" );
  227.         per_per_yr = ask_i( "Number of Periods Per year" );
  228.         if (annuity_type == T_ANNUITY_DUE)
  229.             {
  230.             yes = ask_yn( "Payments at begin of each period" );
  231.             if (!yes)    /* switch if wrong */
  232.                 annuity_type = T_NORMAL_ANN;
  233.             }
  234.         else
  235.             {
  236.             yes = ask_yn( "Payments at end of of each period" );
  237.             if (!yes)
  238.                 annuity_type = T_ANNUITY_DUE;
  239.             }
  240.         }
  241.     else
  242.         usage();
  243.  
  244.     /* common processing to do the computations */
  245.     rate = get_rate( tot_per, pmt, fv, annuity_type );
  246.     rate *= (FLOAT) per_per_yr;
  247.     rate *= (FLOAT)100.0;
  248.     printf("%.8f%% per %s, Payment deposited at %s of each period\n",
  249.     rate, (per_per_yr == 1) ? "period" : "year",
  250.     (annuity_type == T_ANNUITY_DUE) ? "beginning" : "end" );
  251.  
  252.     exit(0);
  253. }
  254.  
  255. usage()
  256. {
  257.     fprintf(stderr, "Usage: %s [-de -t#] [tot_periods pmt_per_period future_value [periods_per_year]]\n", cmd);
  258.     fprintf(stderr, "\t-d = debug\n");
  259.     fprintf(stderr, "\t-e = each payment is at the end of each period (default = begin)\n");
  260.     fprintf(stderr, "\t-t50 = sets the number of Tried guesses to 50, default = %d\n", DEF_TRY);
  261.     fprintf(stderr, "\t is used to make the program run faster, but less accuracy\n");
  262.     exit(1);
  263. }
  264.  
  265.  
  266. /*
  267.  * compute rate per period by using a binary search
  268.  */
  269. FLOAT
  270. get_rate( tot_per, pmt, fv, ann )
  271.     int    tot_per;
  272.     FLOAT    pmt,
  273.         fv;
  274.     int    ann;    /* annuity type */
  275. {
  276.     FLOAT    lo_rate,
  277.         hi_rate,
  278.         mid_rate,
  279.         try_fv;        /* guessed future value */
  280.     int    try;
  281.  
  282.     /* added this because "saverate 4 100 400 4" was returning
  283.      * .00000073% per year mysteriously.
  284.      */
  285.     if (fv == get_fv( tot_per, (FLOAT)0.0, pmt, ann))
  286.         return 0.0;
  287.  
  288.     lo_rate = .01;    /* 1% per period */
  289.     hi_rate = .05;    /* 5% per period */
  290.  
  291.     /* adjust lo_rate down until below fv */
  292.     /* this is because we may have "lost" money */
  293.     while( (try_fv = get_fv( tot_per, lo_rate, pmt, ann )) > fv )
  294.         {
  295.         lo_rate -= .05;
  296. #ifdef DEBUG
  297.         if (debug)
  298.         printf("get_rate: lowering lo_rate to %.2f\n", lo_rate);
  299. #endif
  300.         }
  301.  
  302.     /* adjust hi_rate up until above fv */
  303.     while( (try_fv = get_fv( tot_per, hi_rate, pmt, ann )) < fv )
  304.         {
  305.         hi_rate += .05;
  306. #ifdef DEBUG
  307.         if (debug)
  308.         printf("get_rate: raising hio_rate to %.2f\n", hi_rate);
  309. #endif
  310.         }
  311.  
  312.     /* binary search */
  313.     for ( try = 1; mid_rate = (lo_rate+hi_rate)/2.0; ++try)
  314.         {
  315.         try_fv = get_fv( tot_per, mid_rate, pmt, ann );
  316. #ifdef DEBUG
  317.         if (debug)
  318.         printf("get_rate: trying %lf <= %lf <= %lf\n", lo_rate, mid_rate, hi_rate);
  319. #endif
  320.         if (try_fv < fv )    /* mid_rate is too low ? */
  321.             lo_rate = mid_rate;
  322.         else
  323.             hi_rate = mid_rate;
  324.         if (r_abs( fv - try_fv ) <= .0000000001)
  325.             break;
  326.         else if (try >= MAX_TRY)
  327.             {
  328. #ifdef DEBUG
  329.             if (debug)
  330.                 {
  331.                 printf("Breaking out of loop after %d times: lo=%f, hi = %f\n", try, lo_rate, hi_rate);
  332.                 printf("hi - lo = %f\n", hi_rate - lo_rate);
  333.                 }
  334. #endif
  335.             break;
  336.             }
  337.         }
  338.     return mid_rate;
  339. }
  340.  
  341. /*
  342.  * real number absolute value
  343.  */
  344. FLOAT
  345. r_abs( f )
  346.     FLOAT    f;
  347. {
  348.     if (f >= 0)
  349.         return f;
  350.     else
  351.         return -f;
  352. }
  353.  
  354. /*
  355.  * compute future value using the formula at the beginning of this program
  356.  */
  357. FLOAT
  358. get_fv( tot_per, rate, pmt, ann )
  359.     int    tot_per;
  360.     FLOAT    rate,
  361.         pmt;
  362.     int    ann;    /* annuity type */
  363. {
  364.     FLOAT    one_r,    /* 1+r */
  365.         fv,    /* future value to return */
  366.         num,    /* numerator */
  367.         den;    /* denominator */
  368.  
  369.     /* money is not earning any interest, so prevent "divide by zero" */
  370.     if (rate == 0.0)
  371.         return (pmt * tot_per);
  372.  
  373.     one_r = (FLOAT)1.0 + rate;
  374.     if (ann == T_ANNUITY_DUE)    /* save at begin of each period */
  375.         {
  376.         num = raise( one_r, (tot_per+1) ) - one_r;
  377.         den = rate;
  378.         fv = pmt * (num / den );
  379.         return fv;
  380.         }
  381.     else if (ann == T_NORMAL_ANN)    /* save at end of each period */
  382.         {
  383.         num = raise( one_r, tot_per) - 1.0;
  384.         den = rate;
  385.         fv = pmt * (num / den );
  386.         return fv;
  387.         }
  388.     else
  389.         error("Bad arg (%d) to get_fv()\n", ann);
  390. }
  391.  
  392. /*
  393.  * return f^n
  394.  * where f is FLOAT and n is integer >= 0.
  395.  */
  396. FLOAT
  397. raise( f, n )
  398.     FLOAT    f;
  399.     int    n;
  400. {
  401.     FLOAT    rtn;
  402.  
  403.     if (n < 0)
  404.         error("raise: not written to handle negative exponents");
  405.     for (rtn = 1.0; n > 0; --n)
  406.         rtn *= f;
  407.     return rtn;
  408. }
  409.  
  410. #define MANYARGS msg, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9
  411. typedef char    *CHARSTAR;
  412.  
  413. /*
  414.  * print error message and exit
  415.  * Error message does *not* have a newline at the end.
  416.  */
  417. error(MANYARGS)
  418.     CHARSTAR    MANYARGS;
  419. {
  420.     extern    char    *cmd;
  421.     char    buffer[BUFSIZ];
  422.     sprintf(buffer, MANYARGS);
  423.     fprintf(stderr, "%s: error: %s. Exitting.\n", cmd, buffer);
  424.     exit(1);
  425. }
  426.  
  427.  
  428. /*
  429.  * parses main() arguments, and returns index of first file
  430.  */
  431. get_args( argc, argv )
  432.     int    argc;
  433.     char    *argv[];
  434. {
  435.     char    c;
  436.     int    errflag;
  437.  
  438.     errflag = 0;
  439.  
  440.     while ((c = getopt(argc, argv, "det:")) != EOF)
  441.     switch(c){
  442.     case 'd':
  443.         debug = 1;
  444.         break;
  445.     case 'e':
  446.         annuity_type = T_NORMAL_ANN;
  447.         break;
  448.     case 't':
  449.         MAX_TRY = atoi( optarg );
  450.         break;
  451.     default:
  452.         errflag = 1;
  453.         break;
  454.     }
  455.  
  456.     if (errflag)
  457.         usage();
  458.  
  459.  
  460.     return optind;    /* index of first non-option */
  461. }
  462.  
  463. /*
  464.  * Ask a question and return an integer
  465.  */
  466. ask_i( msg )
  467.     char    *msg;
  468. {
  469.     int    rtn;
  470.     printf("%s ? ", msg );
  471.     fflush(stdout);
  472.     scanf( "%d", &rtn);
  473.     return rtn;
  474. }
  475.  
  476. /*
  477.  * Ask a question and return a floating point number
  478.  * HARD CODED TO ASSUME THAT RETURNS A DOUBLE !!
  479.  */
  480. FLOAT
  481. ask_f( msg )
  482.     char    *msg;
  483. {
  484.     double    rtn;
  485.     printf("%s ? ", msg );
  486.     fflush(stdout);
  487.     scanf( "%lf", &rtn);    /* %lf = double, %f = float */
  488.     return rtn;
  489. }
  490.  
  491. /*
  492.  * Ask a y/n question, and return 1 iff yes.
  493.  */
  494. ask_yn( msg )
  495.     char    *msg;
  496. {
  497.     char    rtn[100];
  498.  
  499.     while(1){
  500.         printf("%s ? (y/n) ", msg);
  501.         fflush(stdout);
  502.         fflush(stdin);    /* strange interaction scanf() & gets() */
  503.         gets( rtn );
  504.         switch( rtn[0] ){
  505.         case 'y':
  506.         case 'Y':
  507.         case '\0':
  508.             return 1;
  509.         case 'n':
  510.         case 'N':
  511.             return 0;
  512.         default:
  513.             continue;
  514.         }/* switch */
  515.     }  /* while */
  516. }
  517. \SHAR_EOF
  518. # ............    F  I   L   E      E  N  D  .......... saverate.c
  519. fi # end of overwriting check
  520. if test -f saverate.help
  521. then
  522. echo shar: will not over-write existing file 'saverate.help'
  523. else
  524. echo x - saverate.help
  525. # ............    F  I   L   E      B  E  G  .......... saverate.help
  526. cat << '\SHAR_EOF' > saverate.help
  527. saverate [-de -t#] [tot_periods pmt_per_period future_value [periods_per_year]]
  528.     -d = debug
  529.     -e = each payment is at the end of each period (default = begin)
  530.     -t50 = sets the number of Tried guesses to 50, default = 100
  531.      is used to make the program run faster, but less accuracy
  532.  
  533. Used to determine the interest rate when you deposit the same payment
  534. per period into a savings account.  The percent rate is the annual
  535. percent per year (if periods_per_year is given), else it is the percent
  536. per period.
  537.  
  538.  
  539. If you supply no numbers, it asks you for all of the numbers.
  540. \SHAR_EOF
  541. # ............    F  I   L   E      E  N  D  .......... saverate.help
  542. fi # end of overwriting check
  543. # end of shell archive
  544. exit 0
  545. -- 
  546. FullName:    Dennis Bednar
  547. UUCP:        {uunet|sundc}!rlgvax!dennis
  548. USMail:        CCI; 11490 Commerce Park Dr.; Reston VA 22091
  549. Telephone:    +1 703 648 3300
  550.